//#include <NTDDK.h>
#include <ifs/wnet/ntifs.h>
//#include <wdm.h>

VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject);
NTSTATUS DriverEntry (
    IN PDRIVER_OBJECT pDriverObject,
    IN PUNICODE_STRING pRegistryPath	);
	
#pragma alloc_text(init, DriverEntry)
#pragma alloc_text(page, DriverUnload)

static PDEVICE_OBJECT dev_obj = NULL;
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject)
{
	if (dev_obj)
	{
		IoDeleteDevice(dev_obj);
		dev_obj = NULL;
	}
	KdPrint(("gate : %s \n", __FUNCTION__));	
}


#pragma   pack(1)  
typedef struct
{
	unsigned short rpl : 2;
	unsigned short ti : 1;
	unsigned short index : 13;
} SELECTOR;

typedef   struct  
{  
	unsigned   short     limit_0_15;  
	unsigned   short     base_0_15;  
	unsigned   char       base_16_23;  
	unsigned   char       accessed         :   1;  
	unsigned   char       readable         :   1;  
	unsigned   char       conforming     :   1;  
	unsigned   char       code_data       :   1;  
	unsigned   char       app_system     :   1;  
	unsigned   char       dpl                   :   2;  
	unsigned   char       present           :   1;  
	unsigned   char       limit_16_19   :   4;  
	unsigned   char       unused             :   1;  
	unsigned   char       always_0         :   1;  
	unsigned   char       seg_16_32       :   1;  
	unsigned   char       granularity   :   1;  
	unsigned   char       base_24_31;  
}   CODE_SEG_DESCRIPTOR;  
   
typedef   struct  
{  
	unsigned   short     offset_0_15;  
	unsigned   short     selector;  
	unsigned   char       param_count   :   4;  
	unsigned   char       some_bits       :   4;  
	unsigned   char       type                 :   4;  
	unsigned   char       app_system     :   1;  
	unsigned   char       dpl                   :   2;  
	unsigned   char       present           :   1;  
	unsigned   short     offset_16_31;  
}   CALLGATE_DESCRIPTOR;  
#pragma   pack()   

#pragma pack(1)

typedef struct
{
    short limit;
	ULONG base;
} IDTR; // GDTR and IDTR format

typedef struct
{
	short offs_l;
	short sel;
	short attrib;
	short offs_h;
} DESC; 

typedef struct
{
	unsigned short limit;
	unsigned short base_l;
	unsigned char base_m;
	unsigned short type;
	unsigned char base_h;
} TSS;
	
#pragma pack()

static NTSTATUS ignore(IN struct _DEVICE_OBJECT *DeviceObject, IN struct _IRP *Irp)
{
	PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);

	IoCompleteRequest(Irp, IO_NO_INCREMENT);	
	return STATUS_SUCCESS;
}

static int find_tss(IDTR gdt)
{
	int i;
	TSS *tss;
	for (i = 0; i < gdt.limit; ++i)
	{
		tss = (TSS *)(gdt.base + i * 8);
		if ((tss->type & 0xf) == 0x09)
			break;
		if ((tss->type & 0xf) == 0x0b)
			break;		
	}
	return (0);
}

#pragma warning(disable:4731)
static void test_func()
{
	unsigned char *p = NULL;
	int i, j;
	p = (unsigned char *)&p;

	for (i = 0; i < 0x5; ++i) {
		for (j = 0; j < 0x10; ++j) {
			KdPrint(("%hhx ", p[i * 0x10 + j]));
		}
		KdPrint(("\n", __FUNCTION__));
	}

	KdPrint(("%s \n", __FUNCTION__));


	__asm {
		mov   esp, ebp;
		pop   ebp  ;
		retf 0;
	}
}

static int find_invalid(IDTR gdt)
{
	int i;
	CALLGATE_DESCRIPTOR *gate;
	for (i = 1; i < gdt.limit; ++i)
	{
		gate = (CALLGATE_DESCRIPTOR *)(gdt.base + i * 8);
		KdPrint(("%s gdt is %08x %08x\n", 
				__FUNCTION__, *(int *)gate, *(((int *)gate) + 1)));					
		if ((gate->present == 0)) {
			return (i);
		}
	}
	return (-1);
}

static int dump_sel(SELECTOR sel, IDTR gdt)
{
//	SELECTOR *sel = (SELECTOR *)&cs;
	CODE_SEG_DESCRIPTOR *cs_desc;

	KdPrint(("%s cs is 0x%02x[0x%02hx]\n", 
			__FUNCTION__, *(short *)&sel, sel.index));
	
	if (sel.index >= gdt.limit)
		return (-1);
	
	cs_desc = (CODE_SEG_DESCRIPTOR *)(gdt.base + sel.index * 8);
	KdPrint(("%s gdt[%02hx] is 0x%08x %08x\n", 
			__FUNCTION__, sel.index, *(int *)cs_desc, *(((int *)cs_desc) + 1)));
		
	KdPrint(("%s cs desc is limit_h[%04x%04x], base[%02x%02x%04x] dpl[%d] presend[%d], accessed[%d], readable[%d], conforming[%d], code_data[%d], app_system[%d]\n", 
			__FUNCTION__, cs_desc->limit_16_19, cs_desc->limit_0_15,
			cs_desc->base_24_31, cs_desc->base_16_23, cs_desc->base_0_15,
			cs_desc->dpl, cs_desc->present, cs_desc->accessed,
			cs_desc->readable, cs_desc->conforming, cs_desc->code_data,
			 cs_desc->app_system));					
	
	
	return (0);
}

static int dump_gate(SELECTOR sel, IDTR gdt)
{
//	SELECTOR *sel = (SELECTOR *)&cs;
	CALLGATE_DESCRIPTOR *cs_desc;

	KdPrint(("%s sel is 0x%02x[0x%02hx]\n", 
			__FUNCTION__, *(short *)&sel, sel.index));
	
	if (sel.index >= gdt.limit)
		return (-1);
	
	cs_desc = (CALLGATE_DESCRIPTOR *)(gdt.base + sel.index * 8);
	KdPrint(("%s gdt[%02hx] is 0x%08x %08x\n", 
			__FUNCTION__, sel.index, *(int *)cs_desc, *(((int *)cs_desc) + 1)));
		
	KdPrint(("%s gate desc is limit[0x%04x%04x], selector[0x%02hx] dpl[%d] present[%d], type[%d], app_system[%d]\n", 
			__FUNCTION__, cs_desc->offset_16_31, cs_desc->offset_0_15,
			cs_desc->selector, cs_desc->dpl, cs_desc->present,
			cs_desc->type, cs_desc->app_system));					
		
	return (0);
}


static NTSTATUS gate_read(IN struct _DEVICE_OBJECT *DeviceObject, IN struct _IRP *Irp)
{
	PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);
	int ret;
	short farcall[3];
	SELECTOR testcs;
	SELECTOR testss;
	SELECTOR testds;
	IDTR testgdt, testidt;
	SELECTOR selector;
	CALLGATE_DESCRIPTOR *callgate;
	
	__asm{
		mov testcs, cs;
		mov testss, ss;
		mov testds, ds;
		sgdt [testgdt];
		sidt [testidt];
	};
	KdPrint(("%s stack at %p, ip at %p\n", 
			__FUNCTION__, &testcs, gate_read));

	ret = find_invalid(testgdt);
	KdPrint(("%s invalid gdt is %d\n", 
			__FUNCTION__, ret));

	selector.ti = 0;
	selector.rpl = 0;
	selector.index = ret & 0x1fff;

	if (selector.index >= testgdt.limit) {
		KdPrint(("%s index[%d] to big\n", 
				__FUNCTION__, selector.index));
		goto done;
	}
	
	farcall[2] = *(short *)&selector;

	KdPrint(("%s cs[%04hx] ss[%04hx] ds[%04hx] farcall[%04hx]\n", 
			__FUNCTION__, testcs, testss, testds, farcall[2]));
/*
	dump_sel(testds, testgdt);	
	dump_sel(testss, testgdt);
	dump_sel(testcs, testgdt);
*/
	callgate = (CALLGATE_DESCRIPTOR *)(testgdt.base + selector.index * 8);
	callgate->offset_0_15 = (unsigned short)(((unsigned long)&test_func) & 0xffff);
	callgate->offset_16_31 = (unsigned short)(((unsigned long)&test_func) >> 16);
	callgate->selector = *(unsigned short *)&testcs;
	callgate->param_count = 0;
	callgate->some_bits = 0;
	callgate->type = 12; //call gate
	callgate->app_system = 0;
	callgate->dpl = 3;
	callgate->present = 1;

	KdPrint(("%s test_func at[%p]\n", 
			__FUNCTION__, test_func));	
	dump_gate(selector, testgdt);	

	_asm {  
		call   fword   ptr   [farcall]  ;
	}

	KdPrint(("%s ret from test_func\n", 
			__FUNCTION__));	
done:
	IoCompleteRequest(Irp, IO_NO_INCREMENT);	
	return STATUS_SUCCESS;
}

NTSTATUS DriverEntry (
    IN PDRIVER_OBJECT pDriverObject,
    IN PUNICODE_STRING pRegistryPath	)
{
	NTSTATUS ret;
	UNICODE_STRING name, link_name;
	char *p;

	KdPrint(("gate : %s \n", __FUNCTION__));
	pDriverObject->DriverUnload = DriverUnload;

	pDriverObject->MajorFunction[IRP_MJ_CREATE] = ignore;
	pDriverObject->MajorFunction[IRP_MJ_CREATE_NAMED_PIPE] = ignore;
	pDriverObject->MajorFunction[IRP_MJ_CREATE_MAILSLOT] = ignore;
	pDriverObject->MajorFunction[IRP_MJ_READ] = gate_read;
	pDriverObject->MajorFunction[IRP_MJ_WRITE] = ignore;
	
	RtlInitUnicodeString(&name, L"\\FileSystem\\testgate");
	ret = IoCreateDevice(pDriverObject, 0, &name, FILE_DEVICE_UNKNOWN, 0, FALSE, &dev_obj);
	if (ret != STATUS_SUCCESS) {
		KdPrint(("gate : %s : IoCreateDevice fail[0x%x]\n", __FUNCTION__, ret));
		return STATUS_SUCCESS;	
	}

	RtlInitUnicodeString(&link_name, L"\\??\\testgate_link");
	IoDeleteSymbolicLink(&link_name);
	ret = IoCreateSymbolicLink(&link_name, &name);
	if (ret != STATUS_SUCCESS) {
		KdPrint(("gate : %s : IoCreateSymbolicLink fail[0x%x]\n", __FUNCTION__, ret));
		return STATUS_SUCCESS;	
	}
	
	SetFlag(dev_obj->Flags, DO_BUFFERED_IO );
	SetFlag(dev_obj->Characteristics, FILE_DEVICE_SECURE_OPEN );
    ClearFlag(dev_obj->Flags, DO_DEVICE_INITIALIZING);
	
	return STATUS_SUCCESS;
}



